﻿using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;

namespace ArtMath.PointCollection
{
    [Serializable]
    public class LineSegment : ContinuityPoint
    {
        public PointD Point1, Point2;
        public LineSegment()
        {

        }
        public LineSegment(PointD pt1, PointD pt2)
        {
            Point1 = pt1;
            Point2 = pt2;
        }
        public override bool Contains(PointD pt)
        {
            return pt.OnLineSegment(Point1, Point2) && AfterContains(pt);
        }
        public override PointD? GetNearestPoint(PointD pt)
        {
            if (Utility.ScalarProduct(Point1, Point2, Point1, pt) <= 0) return Point1;
            if (Utility.ScalarProduct(Point2, Point1, Point2, pt) <= 0) return Point2;
            return pt.GetProjectivePoint(Point1, Point2);
        }
        public override PointD? HeadPoint
        {
            get { return Point1; }
        }
        public override PointD? TailPoint
        {
            get { return Point2; }
        }
        public override double GetSlideExcess(PointD currentLocation, double length)
        {
            if (length > 0)
            {
                return length - currentLocation.DistanceTo(Point2);
            }
            else
            {
                return -length - currentLocation.DistanceTo(Point1);
            }
        }
        public override PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        {
            double len = Point1.DistanceTo(Point2), excess;
            reverse = false;
            length %= 2 * len;
            if (length > 0)
            {
                excess = length - currentLocation.DistanceTo(Point2);
                if (excess <= 0)
                {
                    return currentLocation.Offset(Point1.GetAngle(Point2), length);
                }
                else
                {
                    excess -= len;
                    if (excess <= 0)
                    {
                        return Point1.Offset(Point1.GetAngle(Point2), -excess);
                    }
                    else
                    {
                        return Point1.Offset(Point1.GetAngle(Point2), excess);
                    }
                }
            }
            else
            {
                excess = -length - currentLocation.DistanceTo(Point1);
                if (excess <= 0)
                {
                    return Point1.Offset(Point1.GetAngle(Point2), -excess);
                }
                else
                {
                    if (excess <= len)
                    {
                        return Point1.Offset(Point1.GetAngle(Point2), excess);
                    }
                    else
                    {
                        return Point2.Offset(Point2.GetAngle(Point1), excess - len);
                    }
                }
            }
        }
        protected override PointCollectionBase Intersect(PointCollectionBase collection)
        {
            HashSet<PointD> set = new HashSet<PointD>();
            LineSegment ls = collection as LineSegment;
            if (ls != null)
            {
                set.AddRange(CurveIntersection.LineSegmentsIntersection(Point1, Point2, ls.Point1, ls.Point2));
            }
            else
            {
                Ray ray = collection as Ray;
                if (ray != null)
                {
                    set.AddRange(CurveIntersection.LineSegmentRayIntersection(Point1, Point2, ray.SourcePoint, ray.DirectedAngle));
                }
                else
                {
                    StraightLine sl = collection as StraightLine;
                    if (sl != null)
                    {
                        set.AddRange(CurveIntersection.LineSegmentStraightLineIntersection(Point1, Point2, sl.Location, sl.Slope));
                    }
                    else
                    {
                        PartialCircle arc = collection as PartialCircle;
                        if (arc != null)
                        {
                            set.AddRange(CurveIntersection.LineSegmentArcIntersection(Point1, Point2, arc.Center, arc.Radius, arc.Start, arc.Sweep));
                        }
                        else
                        {
                            Circle cir = collection as Circle;
                            if (cir != null)
                            {
                                set.AddRange(CurveIntersection.LineSegmentCircleIntersection(Point1, Point2, cir.Center, cir.Radius));
                            }
                            else
                            {
                                Ellipse oval = collection as Ellipse;
                                if (oval != null)
                                {
                                    set.AddRange(CurveIntersection.LineSegmentEllipseIntersection(Point1, Point2, oval.Center, oval.LongAxisHalfLength, oval.ShortAxisHalfLength, oval.RotateAngle));
                                }
                                else
                                {
                                    Hyperbola hyp = collection as Hyperbola;
                                    if (hyp != null)
                                    {
                                        set.AddRange(CurveIntersection.LineSegmentHyperbolaIntersection(Point1, Point2, hyp.Center, hyp.RealAxisHalfLength, hyp.ImaginaryAxisHalfLength, hyp.RotateAngle));
                                    }
                                    else
                                    {
                                        Parabola para = collection as Parabola;
                                        if (para != null)
                                        {
                                            set.AddRange(CurveIntersection.LineSegmentParabolaIntersection(Point1, Point2, para.Center, para.FocusDirectrixDistance, para.RotateAngle));
                                        }
                                        else
                                        {
                                            CurveDescribable cd = collection as CurveDescribable;
                                            if (cd != null && cd.Curves != null)
                                            {
                                                foreach (Curve item in cd.Curves)
                                                {
                                                    set.AddRange(CurveIntersection.LineSegmentCurveIntersection(Point1, Point2, item, item.IsClosed));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (set.Count == 0) return Empty.Instance;
            return AfterIntersect(collection.AfterIntersect(new DiscontinuityPoint(set)));
        }
    }
}
